package com.itcast.demo.completablefuture.demo;

import com.itcast.demo.completablefuture.config.MongoThreadPoolConfig;
import com.itcast.demo.completablefuture.config.SmallTool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@Slf4j
public class TestCompletableFuture {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // https://mp.weixin.qq.com/s/H8mfKv6ad04OtQzvppOnaA      (CompletableFuture 详解连接)

        // 使用 runAsync() 开启一个子线程去执行无结果
        // 使用 supplyAsync() 开启一个子线程去执行有返回结果
        // CompletableFuture API 的所有方法都有两个重载的方法一个接受Executor线程池
        /**
         * static CompletableFuture<Void> runAsync (Runnablerunnable)
         * static CompletableFuture<Void> runAsync (Runnablerunnable,Executorexecutor)
         * static<U> CompletableFuture<U> supplyAsync (Supplier<U>supplier)
         * static<U> CompletableFuture<U> supplyAsync (Supplier<U>supplier,Executorexecutor)
         */
//        test1();
        /**
         * thenCompose()和thenApply()方法用于将前一个阶段的结果与下一个阶段进行组合
         * thenApplyAsync()方法在此基础上实现异步执行
         * thenAccept()方法用于处理前一个阶段的结果而不需要返回值
         * thenRun()方法用于在前一个阶段完成后执行一些操作
         */
//        test2();
//        test3();
        // 两个同时执行,等待结果一起返回thenCombine()
//        test4();
        // 两个任务都完成了,不关注执行结果的进行下一步操作runAfterBoth/runAfterBothAsync
//        test5();
        // 两个任务并行进行用快的那个的结果作为后续处理applyToEither\applyToEitherAsync
//        test6();
        // 异常处理 使用 exceptionally() 回调处理异常
//        test7();
//        test8();
        // 使用 handle() 方法处理异常,handle()从异常恢复，无论一个异常是否发生它都会被调用。
//        test9();
        // 使用 whenComplete()/whenCompleteAsync() 方法处理异常,,whenComplete()主要用于执行副作用操作，没有返回值；
        // handle()主要用于处理结果和异常，并返回一个新的结果
        //有异常时执行exceptionally()
//        test10();
        // 批量执行(组合多个CompletableFuture),
        // CompletableFuture.allOf(),所有传入的CompletableFuture都已经完成
        // CompletableFuture.anyOf(),任何一个传入的CompletableFuture已经完成
//        test11();

    }

    private static void test11() {
        SmallTool.printTimeAndThread("小白和小伙伴们进餐厅点菜");
//点菜
        CopyOnWriteArrayList<Dish> dishes = new CopyOnWriteArrayList<>();
        for (int i = 1; i <= 12; i++) {
            Dish dish = new Dish("菜" + i, 1);
            dishes.add(dish);
        }
//做菜
        ArrayList<CompletableFuture> cfList = new ArrayList<>();
        for (Dish dish : dishes) {
            CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> dish.make());
            cfList.add(cf);
        }
//等待所有任务执行完毕
        CompletableFuture.allOf(cfList.toArray(new CompletableFuture[cfList.size()])).join();

        System.out.println("小白和小伙伴们吃完了");
    }

    private static void test10() {
        MongoThreadPoolConfig config = new MongoThreadPoolConfig();
        ThreadPoolTaskExecutor asyncTaskExecutor = config.getAsyncExecutor();
        CompletableFuture.runAsync(() -> System.out.println("执行自己的操作"), asyncTaskExecutor)
                .exceptionally(e -> {
                    log.info("获取浙农码失败{}", e.getMessage());
                    return null;
                });
//需要返回值
        CompletableFuture.runAsync(() -> System.out.println("执行自己的操作"), asyncTaskExecutor)
                .handle((result, ex) -> {
                    if (ex != null) log.info("品种推荐同步执行失败", ex);
                    return result;
                });
//没有返回值
        CompletableFuture.runAsync(() -> System.out.println("执行自己的操作"), asyncTaskExecutor)
                .whenComplete((result, ex) -> {
                    if (ex != null) log.info("品种推荐同步执行失败", ex);
                });
    }

    private static void test9() throws InterruptedException, ExecutionException {
        Integer age = -1;
        CompletableFuture<String> maturityFuture = CompletableFuture.supplyAsync(() -> {
            if (age < 0) {
                throw new IllegalArgumentException("Agecannotbenegative");
            }
            if (age > 18) {
                return "Adult";
            } else {
                return "Child";
            }
        }).handle((res, ex) -> {
            if (ex != null) {
                System.out.println("Oops!Wehaveanexception-" + ex.getMessage());
                return "Unknown!";
            }
            return res;
        });

        System.out.println("Maturity:" + maturityFuture.get());
    }

    private static void test8() throws InterruptedException, ExecutionException {
        Integer age = -1;

        CompletableFuture<String> maturityFuture = CompletableFuture.supplyAsync(() -> {
            if (age < 0) {
                throw new IllegalArgumentException("Agecannotbenegative");
            }
            if (age > 18) {
                return "Adult";
            } else {
                return "Child";
            }
        }).exceptionally(ex -> {
            System.out.println("Oops!Wehaveanexception-" + ex.getMessage());
            return "Unknown!";
        });

        System.out.println("Maturity:" + maturityFuture.get());
    }

    private static void test7() {
        SmallTool.printTimeAndThread("张三走出餐厅，来到公交站");
        SmallTool.printTimeAndThread("等待700路或者800路公交到来");

        CompletableFuture<String> bus = CompletableFuture.supplyAsync(() -> {
            SmallTool.printTimeAndThread("700路公交正在赶来");
            SmallTool.sleepMillis(100);
            return "700路到了";
        }).applyToEither(CompletableFuture.supplyAsync(() -> {
            SmallTool.printTimeAndThread("800路公交正在赶来");
            SmallTool.sleepMillis(200);
            return "800路到了";
        }), firstComeBus -> {
            SmallTool.printTimeAndThread(firstComeBus);
            if (firstComeBus.startsWith("700")) {
                throw new RuntimeException("撞树了……");
            }
            return firstComeBus;
        }).exceptionally(e -> {
            SmallTool.printTimeAndThread(e.getMessage());
            SmallTool.printTimeAndThread("小白叫出租车");
            return "出租车叫到了";
        });

        SmallTool.printTimeAndThread(String.format("%s,小白坐车回家", bus.join()));
    }

    private static void test6() {
        SmallTool.printTimeAndThread("张三走出餐厅，来到公交站");
        SmallTool.printTimeAndThread("等待700路或者800路公交到来");

        CompletableFuture<String> bus = CompletableFuture.supplyAsync(() -> {
            SmallTool.printTimeAndThread("700路公交正在赶来");
            SmallTool.sleepMillis(1);
            return "700路到了";
        }).applyToEither(CompletableFuture.supplyAsync(() -> {
            SmallTool.printTimeAndThread("800路公交正在赶来");
            SmallTool.sleepMillis(3);
            return "800路到了";
        }), firstComeBus -> firstComeBus);

        SmallTool.printTimeAndThread(String.format("%s,小白坐车回家", bus.join()));
    }

    private static void test5() {
        SmallTool.printTimeAndThread("小白进入餐厅");
        SmallTool.printTimeAndThread("小白点了番茄炒蛋+一碗米饭");

        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
            SmallTool.printTimeAndThread("厨师炒菜");
            SmallTool.sleepMillis(200);
            return "番茄炒蛋";
        });
        cf1.runAfterBoth(CompletableFuture.supplyAsync(() -> {
            SmallTool.printTimeAndThread("服务员蒸饭");
            SmallTool.sleepMillis(300);
            return "米饭";
        }), () -> {
            SmallTool.printTimeAndThread("厨师和服务员都执行完了");
        }).join();
        SmallTool.printTimeAndThread("结束");
    }

    private static void test4() {
        SmallTool.printTimeAndThread("小白进入餐厅");
        SmallTool.printTimeAndThread("小白点了番茄炒蛋+一碗米饭");

        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
            SmallTool.printTimeAndThread("厨师炒菜");
            SmallTool.sleepMillis(200);
            return "番茄炒蛋";
        }).thenCombine(CompletableFuture.supplyAsync(() -> {
            SmallTool.printTimeAndThread("服务员蒸饭");
            SmallTool.sleepMillis(300);
            return "米饭";
        }), (dish, rice) -> {
            SmallTool.printTimeAndThread("服务员打饭");
            SmallTool.sleepMillis(100);
            return String.format("%s+%s好了", dish, rice);
        });

        SmallTool.printTimeAndThread("小白在打王者");
        SmallTool.printTimeAndThread(String.format("%s,小白开吃", cf1.join()));
    }

    private static void test3() throws InterruptedException, ExecutionException {
        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return "Rajeev";
                }).thenApply(name -> name + " is a good boy")
                .thenApply(greeting -> greeting + " and he is a good boy");
        System.out.println(cf1.get());
    }

    private static void test2() {
        SmallTool.printTimeAndThread("小白进入餐厅");
        SmallTool.printTimeAndThread("小白点了番茄炒蛋+一碗米饭");

        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
            SmallTool.printTimeAndThread("厨师炒菜");
            SmallTool.sleepMillis(200);
            return "番茄炒蛋";
        }).thenCompose(dish -> CompletableFuture.supplyAsync(() -> {
            SmallTool.printTimeAndThread("服务员打饭");
            SmallTool.sleepMillis(100);
            return dish + "+米饭";
        }));

        SmallTool.printTimeAndThread("小白在打王者");
        SmallTool.printTimeAndThread(String.format("%s好了,小白开吃", cf1.join()));
    }

    private static void test1() throws InterruptedException, ExecutionException {
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            System.out.println("I'llruninaseparatethreadthanthemainthread.");
        });

        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return "Resultoftheasynchronouscomputation";
        });
        System.out.println("结果:" + future1.get());

        Executor executor = Executors.newFixedThreadPool(10);
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return "Resultoftheasynchronouscomputation";
        }, executor);
    }
}
